// Assignment 2 - Dynamic Array.cpp : Defines the entry point for the console application.
//

#include "dynarray.h"

// Constructor
template <typename itemType>
dynarray<itemType>::dynarray( int s ){
	ptr = new itemType[s];
	cap = s;
	num = 0;
}

//copy constructor
template <typename itemType>
dynarray<itemType>::dynarray( const dynarray &rhs) {
	ptr = new itemType[rhs.size()];
	num = rhs.size();

	for (int i = 0; i < rhs.size(); i++) {
		ptr[i] = rhs[i];
	}
}

// copy assignment operator
template <typename itemType>
const dynarray<itemType> &dynarray<itemType>::operator=( const dynarray & rhs) {

	delete [] ptr;

	ptr = new itemType[rhs.size()]; // assign new array, with the size of rhs, to ptr

	std::copy(rhs.ptr, (rhs.ptr + rhs.num), ptr); // copy all elements from the array of rhs, to ptr

	num = rhs.num; // update the size of num to the new size

	return *this;
}

// destructor
template <typename itemType>
dynarray<itemType>::~dynarray(){
	delete [] ptr;
}

template <typename itemType>
void dynarray<itemType>::expand() {

	cap *= 2;
	itemType *temp = new itemType[cap];

	for(int i=0; i<num; i++) { 
		temp[i] = ptr[i];
	}

	delete [] ptr;
	ptr = temp;
}

//The item is stored after the last element. If necessary the array is increased.
template <typename itemType>
void dynarray<itemType>::push_back( itemType item ){

	if(num == cap) { // Checks if array is full, and resizes if necassary
		expand();
	}

	ptr[num++] = item; // Put curret item at the end of the array, indexed by 'num'
}

//Insert x at pos. All items in the range [pos,size]1 are moved one position upwards.
//If necessary the array is increased. If pos are outside [0, size] a proper action2 should be taken.
template <typename itemType>
void dynarray<itemType>::insert( itemType item, int pos ){
	if(num == cap) expand;

	if(pos > num)push_back(item);
	else{
		for(int i = num; i>num; i--) ptr[i] = ptr[i-1];
		ptr[pos] = item;
		num++;
	}
}

//Return the last element in the array. The element is removed from the array.
//If the array is empty, a proper action should be taken
template <typename itemType>
itemType dynarray<itemType>::pop_back( ) throw (out_of_range){
	 
	if (num < 1) throw out_of_range ("Array is Empty");
	else{
		itemType temp = ptr[--num];
		return temp;
	}
}

//Return a non const reference to the content at pos. If pos is outside (0, size) a proper action should be taken
template <typename itemType>
itemType &dynarray<itemType>::at(int pos) const {
	if (pos < 0 || pos > num) {
		throw out_of_range("Out of bounds!"); // throw error
	} else { // return pointer to requested item
		return ptr[pos];
	}
}
	
//Remove item at pos. All items in the range [pos, size} are moved one position downwards.
//If pos is outside [0, size) a proper action should be taken.
template <typename itemType>
itemType dynarray<itemType>::erase( int pos ){
	itemType temp;

	if(pos > num-1)temp = pop_back();
	else{
		temp = ptr[pos];
		num--;
		for(int i = pos; i<num; i++) ptr[i] = ptr[i+1];
	}
	return temp;	
}

//Remove all items.
template <typename itemType>
void dynarray<itemType>::clear( ){
	num = 0;
}
	
//Return the size of the array.
template <typename itemType>
int dynarray<itemType>::size( ) const{
	return num;
}
	
//If the array is empty, return true.
template <typename itemType>
bool dynarray<itemType>::empty( ) const {
	return num==0;
}

/*//Return a non const reference to the content at pos. If pos is outside (0, size) a proper action should be taken
template <typename itemType>
itemType & dynarray<itemType>::at( int pos ) const throw(out_of_range){
	if (pos > num-1 || pos < 0) throw out_of_range ("Out Of Bounds");
	return ptr[pos];
}*/
	
//Overloaded operators

//Non-const: Return reference to the pos (the item at pos may by changed from the application).
//const: Return a const reference to the pos (the item at pos cannot be changed)
template <typename itemType>
itemType & dynarray<itemType>::operator[]( int pos ){ // non const variant
	return ptr[pos];
}
	
// const variant
template <typename itemType>
const itemType &dynarray<itemType>::operator[]( int pos ) const {
	return ptr[pos];
}

// Append two arrays
template <typename itemType>
dynarray dynarray<itemType>::operator+( const dynarray &rhs ){
	dynarray temp(*this);
	temp += rhs;
	return temp;
}

// Append two arrays. Result in the left side array.
template <typename itemType>
dynarray &dynarray<itemType>::operator+=( const dynarray &rhs ){
	for(int i = 0; i < rhs.size(); i++) {
		push_back(rhs[i]);
	}

	return (*this);
}

template <typename itemType>
bool dynarray<itemType>::operator==( const dynarray &rhs ) { // Compare two arrays. Return true if equal.
		
	if (this->num != rhs.size()) { // if the size of the arrays doesn't match (i.e. they're not equal), then return false
		return false;
	}

	for (int i = 0; i< this->num; i++) {
		if (this->ptr[i] != rhs[i]) { // check if the individual elements are equal
			return false; // if they're not, return false
		}
	}

	return true; // the two arrays are equal
}

template <typename itemType>
bool dynarray<itemType>::operator!=( const dynarray &rhs ) { // Compare two arrays. Return true if different
	return !(*this==rhs); //  the "!=" operator is the inverse of the "==" operator
}


//friend ostream &operator<<( ostream &str, const dynarray &rhs ); //Output the content of the array

// other functions
//Return a non const reference to the last element of the array. If the array is empty the reference is undefined
template <typename itemType>
itemType &dynarray<itemType>::back() {
	if (num != 0) {
		return ptr[num-1];
	} else {
		//return itemType();
		return;
	}
}
	

//Return a non const reference to the first element of the array. If the array is empty the reference is undefined
template <typename itemType>
itemType &dynarray<itemType>::front( ) {
	if (num == 0) {
		throw out_of_range("Array is empty!");
	} else {
		return ptr[0];
	}
}

template <typename itemType>
int dynarray<itemType>::capacity( ) const { // Return the capacity of the array
	return cap;
}

// If x is present return the position, else return -1
template <typename itemType>
int dynarray<itemType>::contains( const itemType &item) {
	for(int i = 0; i < num; i++) {
		if (ptr[i] == item) {
			return i;
		}
	}
	return -1;
}

//If newSize is greater than the current size the array is expanded and filled with 'fill' or a proper default value.
//If newSize is lesser than the current size the array is decreased to newSize and the surplus items are destroyed.
template <typename itemType>
void dynarray<itemType>::resize( int newSize, itemType fill = itemType() ) {
	if (newSize < num) {
		itemType *temp = new itemType[newSize];

		for(int i=0; i<newSize; i++) { 
			temp[i] = ptr[i];
		}

		num = cap = newSize;
		delete [] ptr;
		ptr = temp;
	} else if (newSize >= num) {
		itemType *temp = new itemType[newSize];
		std::copy(ptr, (ptr + num), temp); // copy all elements from the array of ptr, to temp
		int numTemp= num;
		for (int i = num; i < newSize; i++) {
			push_back(fill);
		}
		num = numTemp;
		cap = newSize;
		delete [] ptr;
		ptr = temp;
	}
}

//Test if left side array is less than right side array. If the size of the left array are greater than the size
//of the right array false is returned. The content of the arrays are compared from left to right.
//If a left side element is less than the corresponding right side element, true is returned.
//If size() of the left array are reached the comparisons are stopped and false is returned.
template <typename itemType>
bool dynarray<itemType>::operator< ( const dynarray &rhs ) {
	if (num < rhs.num) {
		return true;
	} else if (num > rhs.num) {
		return false;
	} else {
		for (int i = 0; i < num; i++) {
			if (ptr[i] >= rhs[i]) {
				return false;
			}
		}

		return true;
	}
}

	
//Test if left side array is greater than or equal to right side array. The test is performed as !(lha < rhs)
template <typename itemType>
bool dynarray<itemType>::operator<=( const dynarray &rhs ) {
	if (num <= rhs.num) {
		return true;
	} else if (num > rhs.num) {
		return false;
	} else {
		for (int i = 0; i < num; i++) {
			if (ptr[i] > rhs[i]) {
				return false;
			}
		}

		return true;
	}
}

//Test if left side array is greater than right side array. The test is opposite to operator <()
template <typename itemType>
bool dynarray<itemType>::operator> ( const dynarray &rhs ) {
	return !(*this<rhs);
}

//Test if left side array is less than or equal to right side array. The test is performed as !(lha > rhs)
template <typename itemType>
bool dynarray<itemType>::operator>=( const dynarray &rhs ) {
	if (num >= rhs.num) {
		return true;
	} else if (num < rhs.num) {
		return false;
	} else {
		for (int i = 0; i < num; i++) {
			if (ptr[i] < rhs[i]) {
				return false;
			}
		}

		return true;
	}
}